/**
 *
 *  Copyright (c) 2011, AstroDAbis
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *      * Redistributions of source code must retain the above copyright notice,
 *        this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright notice,
 *        this list of conditions and the following disclaimer in the documentation
 *        and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *  OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package org.jiscinvolve.astrodabis.mallow.impl.model.jpa;

import java.util.Date;
import java.util.List;
import java.util.Iterator;

//
// Spring HibernateTemplate.
import org.springframework.orm.hibernate3.HibernateTemplate; 
import org.springframework.orm.hibernate3.support.HibernateDaoSupport; 

//
// Spring Component annotations.
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; 
import org.springframework.beans.factory.annotation.Autowired;  

import org.springframework.context.ApplicationContext;

//
// Bean interfaces.
import org.jiscinvolve.astrodabis.mallow.model.api.Tag;
import org.jiscinvolve.astrodabis.mallow.model.api.TagBean;

import org.jiscinvolve.astrodabis.mallow.model.api.Query;
import org.jiscinvolve.astrodabis.mallow.model.api.QueryBean;
import org.jiscinvolve.astrodabis.mallow.model.api.QueryTag;
import org.jiscinvolve.astrodabis.mallow.model.api.QueryTagBean;

import org.jiscinvolve.astrodabis.mallow.model.api.Result;

import org.jiscinvolve.astrodabis.mallow.model.api.Account;
import org.jiscinvolve.astrodabis.mallow.model.api.AccountManager;

import org.jiscinvolve.astrodabis.mallow.model.api.JoinData;
import org.jiscinvolve.astrodabis.mallow.model.api.JoinTag;
import org.jiscinvolve.astrodabis.mallow.model.api.JoinTagData;

import org.jiscinvolve.astrodabis.mallow.model.tap.TapService;
import org.jiscinvolve.astrodabis.mallow.model.tap.TapServiceBean;

import org.jiscinvolve.astrodabis.mallow.model.tap.TapJoin;

import org.jiscinvolve.astrodabis.mallow.model.tap.TapTable;
import org.jiscinvolve.astrodabis.mallow.model.tap.TapTableBean;

import org.jiscinvolve.astrodabis.mallow.model.jpa.PersistentObject;
import org.jiscinvolve.astrodabis.mallow.model.jpa.PersistentManager;
import org.jiscinvolve.astrodabis.mallow.model.jpa.PersistentCollection;
import org.jiscinvolve.astrodabis.mallow.model.jpa.DuplicateEntryException;

//
// Bean implementations.
import org.jiscinvolve.astrodabis.mallow.impl.model.api.TagImpl;
import org.jiscinvolve.astrodabis.mallow.impl.model.api.QueryImpl;
import org.jiscinvolve.astrodabis.mallow.impl.model.api.QueryTagImpl;
import org.jiscinvolve.astrodabis.mallow.impl.model.api.ResultImpl;
import org.jiscinvolve.astrodabis.mallow.impl.model.api.AccountImpl;

import org.jiscinvolve.astrodabis.mallow.impl.model.tap.TapJoinImpl;
import org.jiscinvolve.astrodabis.mallow.impl.model.tap.TapTableImpl;
import org.jiscinvolve.astrodabis.mallow.impl.model.tap.TapServiceImpl;

/**
 * PersistentObjectManager 'extra' interface, used by PersistentObjectImpl.
 * This puts the the PersistentManagerImpl methods in an interface that
 * Spring can wrap in a transactional proxy.
 *
 * PersistentObjectImpl.save() calls the interface method, which is marked
 * as @Transactional(readOnly=false, propagation=Propagation.REQUIRES_NEW).
 *
 * Without this, PersistentObjectImpl.save() would call the native
 * PersistentManagerImpl.save() method, without going via the Spring proxy.
 * When called from within the DispatcherServlet, the OpenSessionInViewFilter
 * will create a long running read only transaction that covers the whole
 * HTTP request. Calling the native PersistentManagerImpl.save() method
 * without the proxy @Transactional annotation proxy causes an exception
 * when we try to save the object in a read only transaction.
 * 
 * This is a temp fix to get some transactional support in 
 * PersistentObjectManager methods called from PersistentObjectImpl.
 *
 * Ideally, we would want to use @Autowired injection in PersistentObject
 * instances, but that requires spring-aop and AspectJ. 
 *
 * So far we haven't been able to get AOP to work.
 * Runtime weaving requires modification to the Java environment, which makes
 * it difficult to deploy in a standard Tomcat install.
 *
 * Compile time weaving fails because the AspectJ compiler rejects all our classes
 * that use @Override on methods inherited from interfaces rather than base classes.
 *
 */
public interface PersistentManagerExtra
extends PersistentManager
    {

    /**
     * Get our Spring Hibernate template.
     * 
     */
    public HibernateTemplate template();

    /**
     * Get our Spring application context.
     * 
     */
    public ApplicationContext context();

    /*
     * Save a PersistentObject in the database.
     *
     */
    public void save(PersistentObjectImpl object);

    /*
     * Refresh a PersistentObject from the database.
     *
     */
    public void refresh(PersistentObjectImpl object);

    /*
     * Delete a PersistentObject from the database.
     *
     */
    public void delete(PersistentObjectImpl object);

// Account stuff ..

    /**
     * Find an Account by identifier.
     *
    public AccountImpl account(String ident);
     */

// Tag stuff ....

    /**
     * Create a new Tag.
     * @todo Change this to select if exists.
     *
     */
    public TagImpl tag(AccountImpl owner, String name);

    /**
     * Create a new Tag.
     *
     */
    public TagImpl tag(AccountImpl owner, TagBean bean);

    /**
     * Select the tags owned by an Account.
     *
     */
    public Iterable<Tag> tags(AccountImpl owner);

// Query stuff ...

    /**
     * Get a Query by identifier.
     *
     */
    public QueryImpl query(String ident);

    /**
     * Create a new Query.
     *
     */
    public QueryImpl query(AccountImpl owner, Query.Action action, QueryBean bean);

    /**
     * Select the Queries owned by an Account.
     *
     */
    public Iterable<Query> queries(AccountImpl owner);

    /**
     * Run a Query (launches the Query execution in a new Thread).
     *
     */
    public void run(QueryImpl query);


// Refactor as update(Action)

    /**
     * Mark a Query as running.
     *
     */
    public void running(QueryImpl query);

    /**
     * Mark a Query as completed.
     *
     */
    public void completed(QueryImpl query);

    /**
     * Mark a Query as cancelled.
     *
     */
    public void cancelled(QueryImpl query);

    /**
     * Mark a Query as failed.
     *
     */
    public void failed(QueryImpl query);

    /**
     * Select the Results for a Query.
     *
     */
    public Iterable<ResultImpl> results(QueryImpl owner);

    /**
     * Create a new QueryTag for a Query.
     *
     */
    public QueryTagImpl tag(AccountImpl actor, Query query, QueryTagBean bean);

// Service stuff ..

    /**
     * Find a TapService by identifier.
     *
     */
    public TapServiceImpl service(String ident);

    /**
     * Create a new TapService.
     *
     */
    public TapServiceImpl service(AccountImpl owner, TapService.Action action, TapServiceBean bean);

    /**
     * Select the services owned by an Account.
     *
     */
    public Iterable<TapService> services(AccountImpl owner);


// Table stuff ..

    /**
     * Find a TapTable by identifier.
     *
     */
    public TapTableImpl table(String ident);

    /**
     * Create a new TapTable.
     *
     */
    public TapTableImpl table(TapServiceImpl service, TapTable.Action action, TapTableBean bean);

    /**
     * Select a TapTable.
     *
    public TapTableImpl table(TapServiceImpl parent, String ident);
     */

    /**
     * Select the tables for an Account.
     *
     */
    public Iterable<TapTable> tables(AccountImpl owner);

    /**
     * Select the tables for a service.
     *
     */
    public Iterable<TapTable> tables(TapServiceImpl parent);


    /**
     * Manager interface for joins.
     *
     */
    public interface JoinManagerEx
    extends PersistentCollection<TapJoin, String>
        {

        @Override
        public TapJoinImpl select(String ident);

        /**
         * Find a matching join.
         *
         */
        public TapJoinImpl select(JoinData data);

        /**
         * Find a matching join or create a new one.
         *
         */
        public TapJoinImpl create(JoinData data);

        /**
         * Tag a join.
         *
         */
        public JoinTag tag(Account owner, JoinTagData data);
        
        }

    @Override
    public JoinManagerEx joins();

    /**
     * Iterate an Iterator inside a database transaction.
     *
     */
    public void process(Iterator iter);

    }
